/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ChateoController;

import java.net.*;
import java.util.ArrayList;
import ChateoFacadeNI.*;
import ChateoFacadeGUI.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author olga
 * @author adrian
 */
public class Controller {

    /**
     * /Access to the central controller
     */
    private static Controller centralController;
    /**
     * /Access to the ChatViewController
     */
    private static ChatViewController cvController;
    /**
     * /The list of user where each user is defined by an id you can find in
     * its own class
     */
    private UsersList userList;
    /**
     * /The list of messages where each message is defined by an id you can
     * find in its own class
     */
    private ArrayList<ChatMessage> messageList = new ArrayList<ChatMessage>();
    /**
     * /The debugging state will provide some more verbose
     */
    private static boolean debug = true;
    /**
     * /Login of the local user
     */
    private String myLogin = "toto";

    //IDs for a user
    InetAddress addr;
    String login;
    /**
     * /The next two Won't work , will never receive the true login and addr
     */
    User LoggedInUser = new User(addr, login, true);
    User LoggedOutUser = new User(addr, login, false);

    /**
     * /Facades
     */
    private static FacadeGUI facadegui;
    private static FacadeNI facadeni;
    /**
     * /Port : STATIC
     */
    private static int port = 2014;

    public Controller() {
        /**
         * /TODO : WAITING FOR GUI This is the version that will generate the
         * whole system
         */
        Controller.facadegui = new FacadeGUI(this);
        Controller.centralController = this;
    }

    /**
     *
     * @param login is the local user's login
     * @param port is the static assigned port
     * @throws SocketException if the socket is wrong
     * @throws NullPointerException
     * @throws UnknownHostException if the host is unknown
     */
    public Controller(String login, int port) throws SocketException, NullPointerException, UnknownHostException {
        //This is the text version of the Controller
        this.myLogin = login;
        Controller.port = port;
        facadeni = new FacadeNI(this, port);

    }

    /**
     *
     * @param remoteLogin the destination user's login
     * @param remoteAddr the destination user's address
     * @return the local user's login
     * @throws UnknownHostException
     */
    public String newUser(String remoteLogin, InetAddress remoteAddr) throws UnknownHostException {
        String out = "";
        User theNewOne = new User(remoteAddr, remoteLogin, true);
        LoggedInUser.setAddr(remoteAddr);
        LoggedInUser.setLogin(remoteLogin);
        LoggedOutUser.setAddr(remoteAddr);
        LoggedOutUser.setLogin(remoteLogin);

        /**
         * /We check if the user doesn't already exist in our user list. If
         * yes,then we create a new user and add him to the list Send the ACK to
         * the user that just got connected with our ID
         */
        if (!theNewOne.equals(userList.getLocalUser())) {
            if (userList.contains(LoggedInUser)) {
                System.err.println("The user " + LoggedInUser.getLogin() + " already exists in the list.Please enter a new login");
                return ("Error existing user in list");

            } else if (userList.contains(LoggedOutUser)) {

                userList.get(userList.indexOf(LoggedOutUser)).setLoggingState(true);
            } else {
                userList.add(theNewOne);
            }
            try {
                sendAck(myLogin, InetAddress.getByName("255.255.255.255"));
            } catch (SocketException ex) {
                Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
            }

            if (DebugMode()) {
                out = remoteLogin;
            } else {
                out = remoteLogin;
            }
            return out;
        }
        return myLogin;

    }

    /**
     *
     * @param remoteLogin the destination user's login
     * @param remoteAddr the destination user's address
     * @return the local user's login
     * @throws UnknownHostException
     */
    public String newUserAck(String remoteLogin, InetAddress remoteAddr) throws UnknownHostException {
        String out = "";
        User theNewOne = new User(remoteAddr, remoteLogin, true);
        LoggedInUser.setAddr(remoteAddr);
        LoggedInUser.setLogin(remoteLogin);
        LoggedOutUser.setAddr(remoteAddr);
        LoggedOutUser.setLogin(remoteLogin);
        if (!theNewOne.equals(userList.getLocalUser())) {
            /**
             * We check if the user doesn't already exist in our user list. We
             * create a new user Then the goal is to add him to the listl
             */
            if (userList.contains(LoggedInUser)) {
                System.err.println("The user " + LoggedInUser.getLogin() + " already exists in the list.Please enter a new login");
                return ("Error existing user in list");

            } else if (userList.contains(LoggedOutUser)) {

                userList.get(userList.indexOf(LoggedOutUser)).setLoggingState(true);
                System.out.println("Welcome back " + remoteLogin);
                theNewOne = new User(remoteAddr, remoteLogin);
            } else {
                userList.add(theNewOne);
            }
            if (DebugMode()) {
                System.out.println("User: " + userList.get(userList.indexOf(theNewOne)) + "has been added to the user List");
                out = remoteLogin;
            } else {
                out = remoteLogin;
            }
            return out;
        } else {
            return myLogin;
        }
    }

    /**
     * This method is aimed in sending an ACK to the new connected user
     *
     * @param login is the local user's login
     * @param IPaddr is the local user's address
     * @throws UnknownHostException
     * @throws SocketException
     */
    public void sendAck(String login, InetAddress IPaddr) throws UnknownHostException, SocketException {
        facadeni.outgoingAck(IPaddr, login);
    }

    /**
     * This method is to check if a user has left
     *
     * @param remoteLogin the remote user's login
     * @param remoteAddr the remote user's address
     * @return
     */
    public boolean userLeft(String remoteLogin, InetAddress remoteAddr) {
        int slot = -1;
        User LeavingUser = new User(remoteAddr, remoteLogin, true);

        /**
         * Changing the leaving user's status to loggedOut
         */
        if (userList.contains(LeavingUser)) {
            slot = userList.indexOf(LeavingUser);
            userList.get(slot).setLoggingState(false);
            System.out.println("The user " + LeavingUser.getLogin() + " has left");
        } else {
            System.out.println("The user " + remoteLogin + " with the ip " + remoteAddr + " has left but we can't find him");
            System.out.println("These are the current users : " + userList.toString());
        }
        return true;

    }

    /**
     * This method is aimed in receiving messages
     *
     * @param remoteLogin is the remote user's login
     * @param remoteAddr is the remote User's address
     * @param text is the text message to send
     * @param privato is to see if yes or not the message is private
     */
    public void newTextMessage(String remoteLogin, InetAddress remoteAddr, String text, boolean privato) {
        User me = userList.getLocalUser();
        User remoteUser = new User(remoteAddr, remoteLogin, true);
        if (userList.contains(remoteUser)) {

            ChatMessage currentMessage = new ChatMessage(text, remoteUser, me, privato);
            messageList.add(currentMessage);
            userList.get(remoteUser).getMessages().add(currentMessage);
            if (debug) {
                System.out.println(currentMessage);
            }
        } else {
            if (debug) {
                System.out.println("Messsage Received from an unknown user : " + remoteLogin + " : \n" + text);
            }
        }

    }

    /**
     * This method is aimed in Sending messages
     *
     *
     * @param remoteUser
     * @param text is the text message to send
     * @param privato is to see if yes or not the message is private
     * @throws java.net.SocketException
     * @throws java.net.UnknownHostException
     */
    public void sendMessage(User remoteUser, String text, boolean privato) throws SocketException, UnknownHostException {
        User me = userList.getLocalUser();
        if (privato) {

            facadeni.outgoingText(remoteUser.getAddr(), me.getLogin(), text, privato);
            ChatMessage currentMessage = new ChatMessage(text, me, remoteUser, privato);
            messageList.add(currentMessage);
            userList.get(remoteUser).getMessages().add(currentMessage);
            if (debug) {
                System.out.println(currentMessage);
            }

        } else {
            facadeni.outgoingText(InetAddress.getByName("255.255.255.255"), me.getLogin(), text, privato);
            ChatMessage currentMessage = new ChatMessage(text, me, me, privato);
            messageList.add(currentMessage);
            if (debug) {
                System.out.println(currentMessage);
            }
        }

    }

    /*
     * This Method is aimed in Testing the message sending method.
     */
    public void mensajesBasicos(String mensaje, String login, boolean privato) throws SocketException, UnknownHostException {
        User remoteUser = userList.getUserByLogin(login);
        sendMessage(remoteUser, mensaje, privato);
    }

    /**
     * This method is aimed in enabling to connect a localUser.
     *
     * @throws java.net.SocketException
     * @throws java.net.UnknownHostException
     */
    public void connect() throws NullPointerException, SocketException, UnknownHostException {
        try {
            userList = new UsersList(new User(InetAddress.getLocalHost(), myLogin, true));
            facadeni.outgoingHello(InetAddress.getByName("255.255.255.255"), myLogin);
            System.out.println("The user " + myLogin + " has been connected \n");
        } catch (SocketException se) {
            System.out.println("Error in creation");
        }

    }

    /**
     *
     * @param port
     * @param login
     * @throws NullPointerException
     * @throws SocketException
     * @throws UnknownHostException
     */
    public void connectFromGUI(int port, String login) throws NullPointerException, SocketException, UnknownHostException {
        this.myLogin = login;
        this.port = port;
        facadeni = new FacadeNI(this, port);
        try {
            userList = new UsersList(new User(InetAddress.getLocalHost(), myLogin, true));
            facadeni.outgoingHello(InetAddress.getByName("255.255.255.255"), myLogin);
            System.out.println("The user " + myLogin + " has been connected \n");
        } catch (SocketException se) {
            System.out.println("Error in creation");
        }

    }

    /**
     * This method is aimed in enabling to disconnect a localUser.
     *
     * @throws java.net.UnknownHostException
     * @throws java.net.SocketException
     */
    public void disconnect() throws UnknownHostException, SocketException {
        facadeni.outgoingBye(InetAddress.getByName("255.255.255.255"), myLogin);
        System.out.println("Disconnection of the user " + myLogin);
    }

    public UsersList getUserList() {
        return userList;
    }

    public String getMyLogin() {
        return myLogin;
    }

    public static boolean DebugMode() {
        return debug;
    }

    public static Controller getCentralController() {
        return centralController;
    }

    public static void setCentralController(Controller centralController) {
        Controller.centralController = centralController;
    }

    public static boolean isDebug() {
        return debug;
    }

    public static void setDebug(boolean debug) {
        Controller.debug = debug;
    }

    public static FacadeGUI getFacadegui() {
        return facadegui;
    }

    public static void setFacadegui(FacadeGUI facadegui) {
        Controller.facadegui = facadegui;
    }

    public static FacadeNI getFacadeni() {
        return facadeni;
    }

    public static void setFacadeni(FacadeNI facadeni) {
        Controller.facadeni = facadeni;
    }

    public static int getPort() {
        return port;
    }

    public static void setPort(int port) {
        Controller.port = port;
    }

    public ArrayList<ChatMessage> getMessagesByUser(User user) {

        return userList.get(user).getMessages();
    }

    public static ChatViewController getcvController() {
        return (Controller.cvController);

    }

    public static void setcvController(ChatViewController cvController) {
        Controller.cvController = cvController;

    }

}
